package fetcher

import (
    "bytes"
    "context"
    "crypto/tls"
    "encoding/json"
    "fmt"
    "io"
    "net"
    "net/http"
    "net/url"
    "time"

    "github.com/cnssl/cert-deploy-iis/internal/errors"
)

type FileChallenge struct {
    Path    string `json:"path"`
    Content string `json:"content"`
}

type CertData struct {
    Status           string         `json:"status"`
    CommonName       string         `json:"common_name"`
    Cert             string         `json:"cert"`
    IntermediateCert string         `json:"intermediate_cert"`
    ExpiresAt        string         `json:"expires_at"`
    File             *FileChallenge `json:"file,omitempty"`
}

type APIResponse struct {
    Code    int      `json:"code"`
    Message string   `json:"message"`
    Data    CertData `json:"data"`
}

type PostRequest struct {
    CSR              string `json:"csr"`
    CommonName       string `json:"common_name"`
    ValidationMethod string `json:"validation_method,omitempty"`
}

type Fetcher struct{ client *http.Client }

// New 创建 Fetcher，强制使用安全 TLS 与连接池
func New(timeout time.Duration) *Fetcher {
    transport := &http.Transport{
        TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS12},
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
        DialContext: (&net.Dialer{Timeout: 10 * time.Second, KeepAlive: 30 * time.Second}).DialContext,
        ForceAttemptHTTP2:   true,
    }
    return &Fetcher{client: &http.Client{Timeout: timeout, Transport: transport}}
}

// mustHTTPS 校验 API URL 使用 HTTPS
func mustHTTPS(apiURL string) error {
    u, err := url.Parse(apiURL)
    if err != nil { return fmt.Errorf("invalid API URL: %w", err) }
    if u.Scheme != "https" { return fmt.Errorf("API URL must use HTTPS, got: %s", u.Scheme) }
    if u.Host == "" { return fmt.Errorf("API URL must have a valid host") }
    return nil
}

func (f *Fetcher) Info(ctx context.Context, apiURL, referID string) (*CertData, error) {
    if err := mustHTTPS(apiURL); err != nil { return nil, errors.NewNetworkError("invalid API URL", err) }
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, apiURL, nil)
    if err != nil { return nil, errors.NewNetworkError("failed to create request", err) }
    req.Header.Set("Accept", "application/json")
    req.Header.Set("Authorization", "Bearer "+referID)
    resp, err := f.client.Do(req)
    if err != nil { return nil, errors.NewNetworkError("failed to get certificate info", err) }
    defer resp.Body.Close()
    if resp.StatusCode != http.StatusOK { return nil, errors.NewNetworkError(fmt.Sprintf("unexpected status code: %d", resp.StatusCode), nil) }
    const maxResponseSize = 512 * 1024
    body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
    if err != nil { return nil, errors.NewNetworkError("failed to read response body", err) }
    var apiResp APIResponse
    if err := json.Unmarshal(body, &apiResp); err != nil { return nil, errors.NewNetworkError("failed to parse JSON response", err) }
    if apiResp.Code != 1 { return nil, errors.NewNetworkError(fmt.Sprintf("API error: %s", apiResp.Message), nil) }
    return &apiResp.Data, nil
}

func (f *Fetcher) StartOrUpdate(ctx context.Context, apiURL, referID, csrPEM, commonName, validationMethod string) (*CertData, error) {
    if err := mustHTTPS(apiURL); err != nil { return nil, errors.NewNetworkError("invalid API URL", err) }
    reqBody := PostRequest{CSR: csrPEM, CommonName: commonName}
    if validationMethod != "" { reqBody.ValidationMethod = validationMethod }
    bodyData, err := json.Marshal(reqBody)
    if err != nil { return nil, errors.NewNetworkError("failed to marshal request", err) }
    req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(bodyData))
    if err != nil { return nil, errors.NewNetworkError("failed to create request", err) }
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Accept", "application/json")
    req.Header.Set("Authorization", "Bearer "+referID)
    resp, err := f.client.Do(req)
    if err != nil { return nil, errors.NewNetworkError("failed to post CSR", err) }
    defer resp.Body.Close()
    if resp.StatusCode != http.StatusOK { return nil, errors.NewNetworkError(fmt.Sprintf("unexpected status code: %d", resp.StatusCode), nil) }
    const maxResponseSize = 512 * 1024
    body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
    if err != nil { return nil, errors.NewNetworkError("failed to read response body", err) }
    var apiResp APIResponse
    if err := json.Unmarshal(body, &apiResp); err != nil { return nil, errors.NewNetworkError("failed to parse JSON response", err) }
    if apiResp.Code != 1 { return nil, errors.NewNetworkError(fmt.Sprintf("API error: %s", apiResp.Message), nil) }
    return &apiResp.Data, nil
}
